# encoding:utf-8
# brief: This is the main function of MergeBin.
# author: chenzhanzhan

import datetime
import socket
import os
import re
import uuid

class MergeBin:
    def __init__(self):
        pass

    def get_time(self):
        now = datetime.datetime.now()
        now = now.strftime("%Y-%m-%d  %H:%M:%S")
        return now

    def get_computer_name(self):
        computer_name = socket.gethostname()
        return computer_name

    def get_user_name(self):
        user_name = os.getlogin()
        return user_name

    def get_mac_address(self):
        mac_address = ''.join(re.findall('..', '%012x' % uuid.getnode())).upper()
        return mac_address
    
    def get_program_info(self):
        current_time = "Time:" + self.get_time() + " "
        computer_name = "Computer:" + self.get_computer_name()
        user_name = "User:" + self.get_user_name()
        mac_address = "MAC:" + self.get_mac_address()
        total_str = current_time + "," + mac_address + "," + computer_name + "," + user_name
        total_str = total_str[:80]
        return total_str
    
    def get_bin_end(self, bin_path):
        with open(bin_path, "rb") as file:
            file.seek(0, 2)
            file_size = file.tell()
            if file_size < 40:
                return -1
            file.seek(file_size - 40, 0)
            last_40_bytes = file.read(40)
        if last_40_bytes.find(b'START') == -1:
            return -1
        return last_40_bytes
    
    def get_type(self,bin_end):
        if bin_end.find(b'START') == -1:
            return -1
        version = bytes.fromhex(bin_end[12:28].decode('utf-8'))
        return version
    
    def get_version(self,bin_end):
        if bin_end.find(b'START') == -1:
            return -1
        hex_str = bin_end[29:37].decode('utf-8')
        version_int = int(hex_str, 16)
        version = version_int.to_bytes(length=4, byteorder='little')
        return version
    
    def check_param(self, bin_path, merge_path, is_same):
        if not os.path.exists(bin_path):
            print("Bin file not exist")
            return False
        else:
            if not bin_path.endswith(".bin"):
                print("Bin file is not a bin file")
                return False
        if not merge_path.endswith(".bin"):
            print("Merge file is not a bin file")
            return False
        if not isinstance(is_same, int):
            is_same = int(is_same)
            if is_same != 0 and is_same != 1:
                print("is_same should be 0 or 1")
                return False
        return True

def calculate_modbus_crc(data):
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return crc

def main(arg_bin_path, arg_merge_path, arg_is_same):

    # 创建对象
    merge_bin = MergeBin()

    # 参数检查
    if not merge_bin.check_param(arg_bin_path, arg_merge_path, arg_is_same):
        print("Invalid parameter")
        return
    
    # 变量初始化
    bin_path = arg_bin_path
    merge_path = arg_merge_path
    is_same = int(arg_is_same)

    # 读取bin文件信息
    program_info = merge_bin.get_program_info()
    bin_info = merge_bin.get_bin_end(bin_path)
    bin_type = merge_bin.get_type(bin_info)
    bin_version = merge_bin.get_version(bin_info)

    # 获取bin文件大小
    with open(bin_path, "rb") as bin_file:
        bin_file.seek(0, 2)
        bin_size = bin_file.tell()

    # 头文件数据生成
    header_data = b"ARMST103VE" + b"?" + \
        bin_size.to_bytes(4, byteorder='big') + b"?" + \
        is_same.to_bytes(1, byteorder='big') + b"?" + \
        bin_type + b"?" + \
        bin_version + b"?"

    # 写入头文件
    with open(merge_path, 'wb') as merge_file:
        merge_file.seek(0, 0)
        merge_file.write(header_data)
        merge_file.seek(128, 0)
        merge_file.write(program_info.encode('utf-8'))
        merge_file.seek(256, 0)
        with open(bin_path, "rb") as bin_file:
            bin_file.seek(0, 0)
            merge_file.write(bin_file.read())

    # 计算CRC并写入
    with open(merge_path, 'rb') as merge_file:
        crc_data = merge_file.read()
    crc_modbus = calculate_modbus_crc(crc_data)
    with open(merge_path, 'ab') as merge_file:
        merge_file.write(crc_modbus.to_bytes(2, byteorder='big'))


if __name__ == "__main__":
    import sys
    if len(sys.argv) != 4:
        print("Usage: MergeBin.exe <bin_path> <merge_path> <is_same>")
        sys.exit(1)
    bin_path = sys.argv[1]
    merge_path = sys.argv[2]
    is_same = sys.argv[3]
    main(bin_path, merge_path, is_same)

